package checkers3d.logic;

import checkers3d.presentation.GUIContainer;
import checkers3d.presentation.GUILabel;
import checkers3d.presentation.GUIPictureBox;
import checkers3d.presentation.GUIPlayingSurface2D;
import checkers3d.presentation.IDrawable;
import checkers3d.presentation.RenderResourceBitmap;
import checkers3d.presentation.RenderResourcePlayingSurface2D;
import java.awt.Point;
import java.awt.Color;
import java.util.LinkedList;
import java.util.Queue;

/**
 * Creates a generic window for displaying a PlayingSurface3D and a current
 * player. Calculates board positions and generates graphics. Passively keeps 
 * track of two player controllers.
 *
 * @author Ruben Acuna
 */
public class GameBase extends GUIContainer
{
    /*
     * The size of the board in the x axis direction.
     */
    protected int boardSizeX;

    /*
     * The size of the board in the y axis direction.
     */
    protected int boardSizeY;

    /*
     * The number of boards.
     */
    protected int boardSizeZ;

    /*
     * The PlayerController for the home player.
     */
    protected IPlayerController playerHome;

    /*
     * The PlayerController for the visitor player.
     */
    protected IPlayerController playerVisitor;

    /*
     * The winning player, used to determine end game scenario.
     */
    protected Player winner = null;

    /**
     * The current piece's finite state (used for limiting capabilites)
     */
    protected PieceState pieceState;

    /**
     * The number of times a forced piece is clicked (used to end turns)
     */
    protected int clickCount;

    /*
     * A queue that keeps track of the players and whose turn it is.
     */
    private Queue<IPlayerController> playerQueue = new LinkedList<IPlayerController>();
    
    /*
     * The PlayingSurface3D which represents the playing boards.
     */
    protected PlayingSurface3D playingSurface3D;
    
    /**
     * Size of each square (aka tile) in pixels.
     */
    protected final int TILE_SIZE = 48;

    /*
     * An array of the coordinate of where each board image for the game is located.
     */
    protected Point[] boardLocations;

    /**
     * The location of the current player icon.
     */
    private Point PLAYER_ICON;

    /**
     * The GUIPictureBox that display's the current player's icon.
     */
    private GUIPictureBox currentPlayerIcon;

    /**
     * The GUILabel that display's the current player's name.
     */
    private GUILabel currentPlayerName;

   /**
     * Class constructor that creates a GUIContainer and adds components to view
     * a given PlayingSurface3D. Calculates positions of visible 2D board slices
     * and setups up a queue of players. Will set the home player as the current
     * player.
     *
     * @param home The home Player.
     * @param visitor The visiting Player.
     * @param boardSetup A PlayingSurface3D that has been setup for play.
     * @param size The size of the window.
     */
    public GameBase(Player home, Player visitor, Player current,
            PlayingSurface3D boardSetup, Point size)
    {
        super(size);

        //assign arguments.
        this.playingSurface3D = boardSetup;
        this.boardSizeX = boardSetup.getSizeX();
        this.boardSizeY = boardSetup.getSizeY();
        this.boardSizeZ = boardSetup.getSizeZ();

        //calculate board locations.
        computeComponentPositions();

        //create player controllers.
        playerHome = new PlayerControllerLocal(boardSizeX, boardSizeY,
                                               boardLocations,
                                               TILE_SIZE, home);
        playerVisitor = new PlayerControllerLocal(boardSizeX, boardSizeY,
                                                  boardLocations,
                                                  TILE_SIZE, visitor);

        //no one should be winning initally.
        winner = null;

        //no piece should be used at the start of the game.
        pieceState = PieceState.UNUSED;

        //amount of times a forced piece is clicked (used to end turn)
        clickCount = 0;

        //add background and board images.
        setupGraphics();

        //add the gui components for displaying the boards.
        for(int z = 0; z < boardSizeZ; z++)
            add(new GUIPlayingSurface2D(boardLocations[z], playingSurface3D.getSurface2D(z), TILE_SIZE));

        //setup queue.
        setupPlayers(current);

        //set board viewer.
        updateBoardViewer();

        //set current player icon.
        setupPlayersIcon();
    }

    /**
     * Calculates the position on screen of the 2D render components of any
     * given PlayingSurface3D object. Saves the results to boardLocations array.
     * 
     */
    private void computeComponentPositions()
    {
        boardLocations = new Point[playingSurface3D.getSizeZ()];

        //TODO: There are magic numbers here, do not flag them. Nothing here is
        //      final until we decide the board locations for real.

        //one board: center it
        if(boardLocations.length == 1)
        {
            boardLocations[0] = new Point((getSize().x - boardSizeX * TILE_SIZE) / 2,
                                         (getSize().y - boardSizeY * TILE_SIZE) / 2);

            PLAYER_ICON = new Point(getSize().x / 2 - 40, TILE_SIZE / 2);
        }
        //two boards: diagonal
        else if (boardLocations.length == 2)
        {
            boardLocations[0] = new Point(0, getSize().y - boardSizeY * TILE_SIZE);
            boardLocations[1] = new Point(getSize().x - boardSizeX * TILE_SIZE, 0);

            PLAYER_ICON = new Point((boardSizeX - 2) * TILE_SIZE, boardLocations[0].y - TILE_SIZE);
        }
        //three boards: diagonal
        else if (boardLocations.length == 3)
        {
            boardLocations[0] = new Point(0, getSize().y - boardSizeY * TILE_SIZE);
            boardLocations[1] = new Point((getSize().x - boardSizeX * TILE_SIZE) / 2,
                                         (getSize().y - boardSizeY * TILE_SIZE) / 2);
            boardLocations[2] = new Point(getSize().x - boardSizeX * TILE_SIZE, 0);

            PLAYER_ICON = new Point(TILE_SIZE * 2, TILE_SIZE * 3);
        }
        //four boards: corners
        else if (boardLocations.length == 4)
        {
            boardLocations[0] = new Point(0, 0);
            boardLocations[1] = new Point(0, getSize().y - boardSizeY * TILE_SIZE);
            boardLocations[2] = new Point(getSize().x - boardSizeX * TILE_SIZE, 0);
            boardLocations[3] = new Point(getSize().x - boardSizeX * TILE_SIZE, getSize().y - boardSizeY * TILE_SIZE);

            PLAYER_ICON = new Point(getSize().x / 2 - 40, TILE_SIZE / 2);
        }
        //k boards: do whatever
        else
        {
            throw new UnsupportedOperationException("K board support is not implemented yet.");
        }
    }

    /**
     * Adds a screen background and builds board images.
     */
    private void setupGraphics()
    {
        add(new GUIPictureBox("Background.png", new Point()));

        //cache images for black and white squares
        RenderResourceBitmap black = new RenderResourceBitmap("board_tile_black.png");
        RenderResourceBitmap white = new RenderResourceBitmap("board_tile_white.png");

        //loop over all boards and add black and white tiles for each one.
        for (int x = 0; x < boardSizeX; x++)
            for (int y = 0; y < boardSizeY; y++)
                for (int z = 0; z < boardSizeZ; z++)
                {
                    GUIPictureBox picture;
                    Point square = new Point(boardLocations[z].x + x * TILE_SIZE,
                                             boardLocations[z].y + y * TILE_SIZE);

                    if(y % 2 == 0) //y is even
                        if(x % 2 == 0) //x is even
                            picture = new GUIPictureBox(white, square);
                        else //x is odd
                            picture = new GUIPictureBox(black, square);
                    else //y is odd
                        if(x % 2 == 0) //x is even
                            picture = new GUIPictureBox(black, square);
                        else //x is odd
                            picture = new GUIPictureBox(white, square);

                    add(picture);
                }
    }

    /**
     * Adds the current home and visitor players to the player queue, the
     * starting player is passed as a argument.
     */
    private void setupPlayers(Player current)
    {
        if (current == playerVisitor.getPlayer())
        {
            playerQueue.add(playerVisitor);
            playerQueue.add(playerHome);
        }
        else
        {
            playerQueue.add(playerHome);
            playerQueue.add(playerVisitor);
        }
    }

    /**
     * Adds a text label and image icon for the current player to the
     * GUIContainer.
     */
    private void setupPlayersIcon()
    {
        GUILabel currentPlayerLabel = new GUILabel("Player: ", Color.WHITE, PLAYER_ICON);
        
        //these two lines attempt to calculate an offset to center the icon.
        int x = currentPlayerLabel.getRenderResource().getWidth();
        int y = (currentPlayerLabel.getRenderResource().getHeight() - getCurrentPlayer().getCheckerRR().getHeight()) / 2;

        Point iconLocation = new Point (PLAYER_ICON.x + x, PLAYER_ICON.y + y);
        
        currentPlayerIcon = new GUIPictureBox((RenderResourceBitmap)getCurrentPlayer().getCheckerRR(), iconLocation);
        currentPlayerName = new GUILabel(getCurrentPlayer().getName(), Color.WHITE, new Point(PLAYER_ICON.x+x+54, PLAYER_ICON.y));

        add(currentPlayerIcon);
        add(currentPlayerLabel);
        add(currentPlayerName);
    }

    /**
     * Returns the current PlayerController.
     *
     * @return Current PlayerController.
     */
    protected IPlayerController getCurrentPlayerController()
    {
        return playerQueue.peek();
    }

    /**
     * Returns the current Player.
     *
     * @return Current Player.
     */
    protected Player getCurrentPlayer()
    {
        return playerQueue.peek().getPlayer();
    }

    /**
     * Sets the object viewing the visible boards to be the current
     * PlayerController's player.
     */
    protected void updateBoardViewer()
    {
        //set the current player as the view of all GUIPlayingSurface2Ds.
        for(IDrawable component : getComponentsList())
            if(component instanceof GUIPlayingSurface2D)
                ((RenderResourcePlayingSurface2D)component.getRenderResource()).setViewer(getCurrentPlayer());
    }

    /**
     * Sets all the overlays in PlayingSurface3D to be blank.
     */
    protected void clearOverlays()
    {
        for (int x = 0; x < boardSizeX; x++)
            for (int y = 0; y < boardSizeY; y++)
                for (int z = 0; z < boardSizeZ; z++)
                    playingSurface3D.setOverlay(PlayingSurfacePositionOverlay.NONE, new Point3D(x, y, z));
    }

    /**
     * Changes the current player controller to be the next one in the queue.
     * Clears the overlays, cycles the queue, updates the current player icon,
     * and sets that player as the viewer of the board.
     */
    protected void nextPlayerController()
    {
        clearOverlays();

        playerQueue.add(playerQueue.remove());

        //update update the icon for the current player
        currentPlayerIcon.setRenderResource(getCurrentPlayer().getCheckerRR());
        currentPlayerName.setText(getCurrentPlayer().getName());
        //set the current player as the view of all GUIPlayingSurface2Ds.
        updateBoardViewer();
    }

    /**
     * Returns a string representation of this object containing it's class name.
     *
     * @return String representation.
     */
    @Override
    public String toString()
    {
        return getClass().getName();
    }
}